/***************************************************************************
 *   Copyright (C) 2009 by Bernhard Neuhofer   *
 *   mail@bneu.at   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#ifndef LOG_H
#define LOG_H

#include <sstream>
#include <string>
#include <stdio.h>

using namespace std;

inline string NowTime();

enum TLogLevel {logERROR, logWARNING, logINFO, logDEBUG};

/**
* Class for logging with 4 different logging levels. Default is INFO
*
*/
template <typename T>
class Log
{
	public:
		Log();
		virtual ~Log();
		std::ostringstream& Get ( TLogLevel level = logINFO );
	public:
		static TLogLevel& ReportingLevel();
		static string ToString ( TLogLevel level );
		static TLogLevel FromString ( const string& level );
	protected:
		std::ostringstream os;
	private:
		Log ( const Log& );
		Log& operator = ( const Log& );
};

template <typename T>
Log<T>::Log()
{
}

template <typename T>
ostringstream& Log<T>::Get ( TLogLevel level )
{
	os << "- " << NowTime();
	os << " " << ToString ( level ) << ": ";
	return os;
}

template <typename T>
Log<T>::~Log()
{
	os << std::endl;
	T::Output ( os.str() );
}

template <typename T>
TLogLevel& Log<T>::ReportingLevel()
{
	static TLogLevel reportingLevel = logDEBUG;
	return reportingLevel;
}

template <typename T>
string Log<T>::ToString ( TLogLevel level )
{
	static const char* const buffer[] = {"ERROR", "WARNING", "INFO", "DEBUG"};
	return buffer[level];
}

template <typename T>
TLogLevel Log<T>::FromString ( const std::string& level )
{

	if ( level == "DEBUG" )
		return logDEBUG;
	if ( level == "INFO" )
		return logINFO;
	if ( level == "WARNING" )
		return logWARNING;
	if ( level == "ERROR" )
		return logERROR;
	Log<T>().Get ( logWARNING ) << "Unknown logging level '" << level << "'. Using INFO level as default.";
	return logINFO;
}

class Output2FILE
{
	public:
		static FILE*& Stream();
		static void Output ( const string& msg );
};

inline FILE*& Output2FILE::Stream()
{
	static FILE* pStream = stderr;
	return pStream;
}

inline void Output2FILE::Output ( const string& msg )
{
	FILE* pStream = Stream();
	if ( !pStream )
		return;
	fprintf ( pStream, "%s", msg.c_str() );
	fflush ( pStream );
}

#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
#   if defined (BUILDING_FILELOG_DLL)
#       define FILELOG_DECLSPEC   __declspec (dllexport)
#   elif defined (USING_FILELOG_DLL)
#       define FILELOG_DECLSPEC   __declspec (dllimport)
#   else
#       define FILELOG_DECLSPEC
#   endif // BUILDING_DBSIMPLE_DLL
#else
#   define FILELOG_DECLSPEC
#endif // _WIN32

class FILELOG_DECLSPEC FILELog : public Log<Output2FILE> {};
//typedef Log<Output2FILE> FILELog;

#ifndef FILELOG_MAX_LEVEL
#define FILELOG_MAX_LEVEL logDEBUG
#endif

#define FILE_LOG(level) \
    if (level > FILELOG_MAX_LEVEL) ;\
    else if (level > FILELog::ReportingLevel() || !Output2FILE::Stream()) ; \
    else FILELog().Get(level)

#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__)

#include <windows.h>

inline string NowTime()
{
	const int MAX_LEN = 200;
	char buffer[MAX_LEN];
	if ( GetTimeFormatA ( LOCALE_USER_DEFAULT, 0, 0,
	                      "HH':'mm':'ss", buffer, MAX_LEN ) == 0 )
		return "Error in NowTime()";

	char result[100] = {0};
	static DWORD first = GetTickCount();
	std::sprintf ( result, "%s.%03ld", buffer, ( long ) ( GetTickCount() - first ) % 1000 );
	return result;
}

#else

#include <sys/time.h>

inline string NowTime()
{
	char buffer[11];
	time_t t;
	time ( &t );
	tm r = {0};
	strftime ( buffer, sizeof ( buffer ), "%X", localtime_r ( &t, &r ) );
	struct timeval tv;
	gettimeofday ( &tv, 0 );
	char result[100] = {0};
	sprintf ( result, "%s.%03ld", buffer, ( long ) tv.tv_usec / 1000 );
	return result;
}

#endif //WIN32

#endif //LOG_H
